package com.ezops.poc.validations.parser;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;

import javax.annotation.PostConstruct;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.stereotype.Component;

import com.ezops.poc.bo.AssetLookUp;
import com.ezops.poc.util.Report;
import com.ezops.poc.util.Report.Row;
import com.ezops.poc.validations.validators.IValidators;

/**
 * Parse the file named ezopsValidation.xls present in class path
 * (META_INF/ezopsValidation.xls) Load and parse the file and create the
 * validation rules based on that.
 * 
 * @author vgaurav
 * 
 */
@Component
public class ValidationFileParser {

	private static final Logger LOGGER = LoggerFactory
			.getLogger(ValidationFileParser.class);

	@Autowired
	private AssetLookUp assetLookUp;

	// file vs validator mapping
	private List<LinkedList<IValidators>> ezopsRules;

	private static Map<String, String> nameValidatorMapping = new HashMap<>();

	// @Inject
	// private RulesServiceLoader rulesServiceLoader;
	public final static String RULEFILEDIR = System.getProperty("user.home")
			+ File.separator + "EZOPS";

	public final static String EZOPS_RULE_SET = RULEFILEDIR + File.separator
			+ File.separator + "ezopsValidation.xlsx";

	@PostConstruct
	protected void init() throws InstantiationException,
			IllegalAccessException, IOException, ClassNotFoundException {

		LOGGER.info("############ - Initializing - ############");
		loadConfig();
		ezopsRules = buildRules(EZOPS_RULE_SET);

		LOGGER.info("############ - Initialization completed - ############");
	}

	public List<LinkedList<IValidators>> getAllValidators() {
		return ezopsRules;
	}

	/**
	 * @throws IOException
	 * 
	 */
	private void loadConfig() throws IOException {
		Resource resource = new ClassPathResource(
				"/META-INF/ezopsValidationConfig.properties");
		Properties props = PropertiesLoaderUtils.loadProperties(resource);
		Iterator<Entry<Object, Object>> propIterator = props.entrySet()
				.iterator();
		while (propIterator.hasNext()) {
			Entry entry = propIterator.next();
			nameValidatorMapping.put(entry.getKey().toString(), entry
					.getValue().toString());
		}

	}

	/**
	 * Read the validation rule file and prepare the mapping.
	 * 
	 * @param ezopsRuleSet
	 * @return
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws ClassNotFoundException
	 */
	private List<LinkedList<IValidators>> buildRules(String ezopsRuleSet)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException {
		List<LinkedList<IValidators>> rules = new ArrayList<LinkedList<IValidators>>();
		try {
			Report report = new Report(new File(EZOPS_RULE_SET));
			Iterator<Row> iterator = report.iterator();
			while (iterator.hasNext()) {
				Row row = iterator.next();
				Map<Integer, String> rowVal = row.getValues();
				rules.add(parseFieldRuleRow(rowVal, report.getColumns()));

			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return rules;
	}

	/**
	 * Parse the validation file rules. The rules are parsed and stored in
	 * LinkedList. This belongs to one row in the file. e.g.
	 ***************************************************************************** 
	 * <table>
	 * <th>
	 * <td>Rule</td>
	 * <td>Asset Type</td>
	 * <td>Mandatory</td>
	 * <td>Type</td>
	 * <td>Range</td>
	 * <td>DB Look Up</td></th>
	 * <tbody>
	 * <tr>
	 * <td>Filed Name</td>
	 * <td>Bond, Equity</td>
	 * <td>Y</td>
	 * <td>Alphanumeric(0-10 chars)</td>
	 * <td>0-100</td>
	 * <td>CUD</td>
	 * </tr>
	 * </tbody>
	 * </table>
	 * 
	 * *************************************************************************
	 * ***
	 * 
	 * @param rowVal
	 * @param headers
	 * @return
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 */
	private LinkedList<IValidators> parseFieldRuleRow(Map rowVal,
			String[] headers) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException {

		LinkedList<IValidators> validators = new LinkedList<>();
		Set<Map.Entry<Integer, String>> entrySet = rowVal.entrySet();
		String pojoField = null;
		List<Integer> applicableAssets = new ArrayList<>();
		for (Map.Entry<Integer, String> entry : entrySet) {
			int key = entry.getKey();
			if (key == 0) {
				 pojoField = entry.getValue();
				// TODO - Chnage this to use EzopsDislayField
//				pojoField = Character.toLowerCase(fieldName.charAt(0))
//						+ fieldName.substring(1, fieldName.length());
				continue;
			}

			if (entry.getValue() == null || entry.getValue().isEmpty()) {
				// rule doesn't apply on this field
				continue;
			}

			String currentHeader = headers[key];

			if (currentHeader.equalsIgnoreCase("Asset Types")) {
				applicableAssets = getAllAssets(entry.getValue());
				continue;
			}

			String validatorName = nameValidatorMapping.get(headers[key]);
			if (validatorName == null || validatorName.equals("")) {
				continue;
			}
			Class validatorClassName = Class.forName(validatorName);
			IValidators validatorInstance = (IValidators) validatorClassName
					.newInstance();
			validatorInstance.init(pojoField, applicableAssets,
					entry.getValue());
			validators.add(validatorInstance);

		}
		return validators;
	}

	private List<Integer> getAllAssets(String value) {

		if (value == null || value.trim().isEmpty()) {
			// application for all assets
			return null;
		}
		String[] assets = value.split(",");

		List<Integer> assetsList = new ArrayList<>(assets.length);
		for (String asset : assets) {
			assetsList.add(assetLookUp.lookUp(asset.trim()));
		}
		return assetsList;
	}

}
